package msg

import (
	"doudizhu/utils"
	"errors"
	"fmt"
	"sort"
	"strconv"
	"sync"
)

var allrooms *Rooms

func init() {
	allrooms = new(Rooms)
	allrooms.roompls = make(map[string]*Room)
	allrooms.plrooms = make(map[string]string)
}

type JoinRoom struct {
	Roomid string
	Userid string
}

type GameStart struct {
	Roomid string
}

type Room struct {
	Roomid  string
	IsStart bool
	//3个玩家斗地主
	Players [3]*Player
	//下标012谁是地主
	Dizhu int
	//地主的3张牌
	RestCard [3]Card
	//上一次出牌，可能为长度为0的数组，不能为nil(在开始游戏之前为nil)
	BeforeChupai []Card
	//下一个出牌者的下标
	NextPlayer int
	//上一个出牌者的ID
	ChupaiId string
	//由于xx出的牌而导致有人要不起
	PassBec  int
	roomLock sync.Mutex
}

func (r *Room) StartGame() error {
	var err error
	r.roomLock.Lock()
	for _, o := range r.Players {
		if o == nil {
			err = errors.New("玩家未到齐，无法开局")
			break
		}
		if !o.ReadyStart {
			err = errors.New("玩家未准备！")
			break
		}
	}
	if r.IsStart {
		err = errors.New("游戏已经开始，请勿重复操作")
	}
	if err != nil {
		r.roomLock.Unlock()
		return err
	}
	r.IsStart = true
	r.roomLock.Unlock()
	cards := RandomCards()
	fmt.Println(cards)
	dizhui := 99
	c30, c31, c32, c33 := 0, 0, 0, 0
	for i, o := range cards {
		if o.Num == 3 {
			switch o.Types {
			case 0:
				c30 = i
			case 1:
				c31 = i
			case 2:
				c32 = i
			case 3:
				c33 = i
			}
		}
	}
	switch {
	case c30 < 51:
		dizhui = c30 / 17
	case c31 < 51:
		dizhui = c31 / 17
	case c32 < 51:
		dizhui = c32 / 17
	case c33 < 51:
		dizhui = c33 / 17
	}
	r.Dizhu = dizhui
	r.Players[0].Cards = cards[:17]
	r.Players[1].Cards = cards[17:34]
	r.Players[2].Cards = cards[34:51]
	r.RestCard = [3]Card{cards[51], cards[52], cards[53]}
	r.Players[dizhui].Cards = append(r.Players[dizhui].Cards, cards[51], cards[52], cards[53])
	fmt.Println(r.Players[0].ShowCards())
	fmt.Println(r.Players[1].ShowCards())
	fmt.Println(r.Players[2].ShowCards())
	r.NextPlayer = dizhui
	r.BeforeChupai = []Card{}
	return nil
}

//无err返回表示可以出牌
func (r *Room) CalCards(cs []Card) error {
	if len(r.BeforeChupai) == 0 {
		return nil
	}
	//王炸->炸弹->普通
	beforetype := r.calCardsType(r.BeforeChupai)
	if beforetype == CARDS_TYPE_KBOOM {
		return errors.New("王炸最大，无法出牌")
	}
	aftertype := r.calCardsType(cs)
	if aftertype == CARDS_TYPE_KBOOM {
		return nil
	}
	if beforetype != aftertype {
		//炸弹炸普通
		if aftertype == CARDS_TYPE_BOOM {
			return nil
		}
		return errors.New("不同类型不能出牌！")
	}
	//出牌类型相同，开始进行大小比较
	if len(cs) != len(r.BeforeChupai) {
		return errors.New("不同类型不能出牌！")
	}
	tmp1 := r.cards2map(r.BeforeChupai)
	tmp2 := r.cards2map(cs)
	switch beforetype {
	case CARDS_TYPE_SINGLE, CARDS_TYPE_DOUBLE, CARDS_TYPE_3ON0, CARDS_TYPE_BOOM:
		if cs[0].Num < r.BeforeChupai[0].Num {
			return errors.New("小牌无法打过大牌")
		}
	case CARDS_TYPE_3ON1, CARDS_TYPE_3ON2, CARDS_TYPE_4ON1, CARDS_TYPE_4ON2, CARDS_TYPE_MULT3ON1, CARDS_TYPE_MULT3ON2,
		CARDS_TYPE_MULT4ON1, CARDS_TYPE_MULT4ON2:
		oldmanynums := make([]int, 0, 0)
		newmanynums := make([]int, 0, 0)
		for k, v := range tmp1 {
			if v > 2 {
				oldmanynums = append(oldmanynums, k)
			}
		}
		for k, v := range tmp2 {
			if v > 2 {
				newmanynums = append(newmanynums, k)
			}
		}
		sort.Ints(oldmanynums)
		sort.Ints(newmanynums)
		if newmanynums[0] < oldmanynums[0] {
			return errors.New("小牌无法打过大牌")
		}
	case CARDS_TYPE_LINE, CARDS_TYPE_MULTDOUBLE, CARDS_TYPE_MULT3ON0:
		oldmanynums := make([]int, 0, 0)
		newmanynums := make([]int, 0, 0)
		for k, _ := range tmp1 {
			oldmanynums = append(oldmanynums, k)
		}
		for k, _ := range tmp2 {
			newmanynums = append(newmanynums, k)
		}
		sort.Ints(oldmanynums)
		sort.Ints(newmanynums)
		if newmanynums[0] < oldmanynums[0] {
			return errors.New("小牌无法打过大牌")
		}
	}
	return nil
}

func (r *Room) CalFinish() {
	for _, p := range r.Players {
		if len(p.Cards) == 0 {

		}
	}
}

func (r *Room) cards2map(cs []Card) map[int]int {
	tmp := make(map[int]int)
	for _, o := range cs {
		i, ok := tmp[o.Num]
		if ok {
			tmp[o.Num] = i + 1
		} else {
			tmp[o.Num] = 1
		}
	}
	return tmp
}

func (r *Room) calCardsType(cs []Card) int {
	tmp := r.cards2map(cs)
	if len(cs) >= 5 && isLine(cs) {
		return CARDS_TYPE_LINE
	}
	num1, num2, num3, num4 := 0, 0, 0, 0
	linenums := make([]Card, 0, 0)
	for k, o := range tmp {
		switch o {
		case 1:
			num1++
		case 2:
			num2++
		case 3:
			num3++
			linenums = append(linenums, Card{Num: k})
		case 4:
			num4++
			linenums = append(linenums, Card{Num: k})
		}
	}
	//顺子全部在进入之前统一处理了，后续不需要继续处理
	switch len(cs) {
	case 1: //单张
		return CARDS_TYPE_SINGLE
	case 2: //对子，王炸
		if len(tmp) == 1 {
			return CARDS_TYPE_DOUBLE
		}
		if (cs[0].Num == 16 || cs[0].Num == 17) && (cs[1].Num == 16 || cs[1].Num == 17) {
			return CARDS_TYPE_KBOOM
		}
	case 3: //3不带
		if len(tmp) == 1 {
			return CARDS_TYPE_3ON0
		}
	case 4: //炸弹，3带1
		if len(tmp) == 1 {
			return CARDS_TYPE_BOOM
		}
		if len(tmp) == 2 {
			for _, v := range tmp {
				if v != 1 && v != 3 {
					return CARDS_TYPE_ERROR
				}
			}
			return CARDS_TYPE_3ON1
		}
	case 5: //顺子，3带2，4带1
		if len(tmp) == 2 {
			for _, v := range tmp {
				if v == 1 || v == 4 {
					return CARDS_TYPE_4ON1
				} else {
					return CARDS_TYPE_3ON2
				}
			}
		}
	case 6: //顺子，3连对，4带2，2个3不带
		if len(tmp) == 3 {
			for _, v := range tmp {
				if v != 2 {
					return CARDS_TYPE_ERROR
				}
			}
			if isMultDouble(cs) {
				return CARDS_TYPE_MULTDOUBLE
			}
		}
		if len(tmp) == 2 {
			if isLine(linenums) {
				for _, v := range tmp {
					if v == 2 {
						return CARDS_TYPE_4ON2
					}
					if v == 3 {
						return CARDS_TYPE_3ON0
					}
				}
			}
		}
	case 7: //顺子
		if isLine(cs) {
			return CARDS_TYPE_LINE
		}
	case 8: //顺子，2个3带1，4连对
		if len(tmp) == 4 {
			if num2 == 4 && isMultDouble(cs) {
				return CARDS_TYPE_MULTDOUBLE
			}
			if num1 == num3 && num1 == 2 && isLine(linenums) {
				return CARDS_TYPE_MULT3ON1
			}
		}
	case 9: //顺子，3个3不带
		for _, v := range tmp {
			if v != 3 {
				return CARDS_TYPE_ERROR
			}
		}
		if isLine(linenums) {
			return CARDS_TYPE_MULT3ON0
		}
	case 10: //顺子，5连对，2个3带2，2个4带1
		if num2 == 5 && isMultDouble(cs) {
			return CARDS_TYPE_MULTDOUBLE
		}
		if num2 == num3 && num2 == 2 && isLine(linenums) {
			return CARDS_TYPE_MULT3ON2
		}
		if num4 == num1 && num1 == 2 && isLine(linenums) {
			return CARDS_TYPE_MULT4ON1
		}
	case 11: //顺子
	case 12: //顺子(最大的顺子，3-a)，6连对，3个3带1，2个4带2，4个3不带
		if num2 == 6 && isMultDouble(cs) {
			return CARDS_TYPE_MULTDOUBLE
		}
		if num3 == num1 && num3 == 3 && isLine(linenums) {
			return CARDS_TYPE_MULT3ON1
		}
		if num3 == 4 && isLine(linenums) {
			return CARDS_TYPE_MULT3ON0
		}
		if num4 == num2 && num2 == 2 && isLine(linenums) {
			return CARDS_TYPE_MULT4ON2
		}
	case 13: //错误
	case 14: //7连对
		if num2 == 7 && isMultDouble(cs) {
			return CARDS_TYPE_MULTDOUBLE
		}
	case 15: //3个3带2，5个3不带，3个4带1
		if num3 == num2 && num3 == 3 && isLine(linenums) {
			return CARDS_TYPE_MULT3ON2
		}
		if num3 == 5 && isLine(linenums) {
			return CARDS_TYPE_MULT3ON0
		}
		if num1 == num4 && num1 == 3 && isLine(linenums) {
			return CARDS_TYPE_MULT4ON1
		}
	case 16: //8连对，4个3带1
		if num2 == 8 && isMultDouble(cs) {
			return CARDS_TYPE_MULTDOUBLE
		}
		if num3 == num1 && num1 == 4 && isLine(linenums) {
			return CARDS_TYPE_MULT3ON1
		}
	case 17: //错误
	case 18: //9连对，3个4带2,6个3不带，限地主
		if num2 == 9 && isMultDouble(cs) {
			return CARDS_TYPE_MULTDOUBLE
		}
		if num3 == 6 && isLine(linenums) {
			return CARDS_TYPE_MULT3ON0
		}
		if num4 == num2 && num2 == 3 && isLine(linenums) {
			return CARDS_TYPE_MULT4ON2
		}
	case 19: //错误
	case 20: //10连对，4个3带2，5个3带1，4个4带1，限地主
		if num2 == 10 && isMultDouble(cs) {
			return CARDS_TYPE_MULTDOUBLE
		}
		if num2 == num3 && num3 == 4 && isLine(linenums) {
			return CARDS_TYPE_MULT3ON2
		}
		if num3 == num1 && num1 == 5 && isLine(linenums) {
			return CARDS_TYPE_MULT3ON1
		}
		if num4 == num1 && num1 == 4 && isLine(linenums) {
			return CARDS_TYPE_MULT4ON1
		}
	default:
		return CARDS_TYPE_ERROR
	}
	return CARDS_TYPE_ERROR
}

func isLine(cs []Card) bool {
	ints := make([]int, 0, 0)
	for _, o := range cs {
		if o.Num == 15 || o.Num == 16 || o.Num == 17 {
			return false
		}
		ints = append(ints, o.Num)
	}
	sort.Ints(ints)
	for i, o := range ints {
		if i == 0 {
			continue
		}
		if o-1 != ints[i-1] {
			return false
		}
	}
	return true
}

func isMultDouble(cs []Card) bool {
	tmp := make(map[int]int)
	for _, o := range cs {
		i, ok := tmp[o.Num]
		if ok {
			tmp[o.Num] = i + 1
		} else {
			tmp[o.Num] = 1
		}
	}
	c1 := make([]Card, 0, 0)
	for k, v := range tmp {
		if v != 2 {
			return false
		}
		c1 = append(c1, Card{Num: k})
	}
	return isLine(c1)
}

type Rooms struct {
	roomrwlock sync.RWMutex
	//房间号对应房间对象
	roompls map[string]*Room
	//用户ID对应房间号
	plrooms map[string]string
}

func GetAllRooms() *Rooms {
	return allrooms
}

func (r *Rooms) NewRoom(p1 *Player) string {
	rid, _ := utils.NewId()
	idstr := strconv.FormatInt(rid, 10)
	room := new(Room)
	room.Roomid = idstr
	room.Players = [3]*Player{p1}
	p1.RoomId = idstr
	p1.Room = room
	r.roomrwlock.Lock()
	r.roompls[idstr] = room
	r.plrooms[p1.UserId] = idstr
	r.roomrwlock.Unlock()
	return idstr
}

func (r *Rooms) JoinRoom(p *Player, rid string) {
	r.roomrwlock.RLock()
	m, ok := r.roompls[rid]
	lengths := 10
	if ok {
		for i, o := range m.Players {
			if o == nil {
				lengths = i
				break
			}
		}
	}
	r.roomrwlock.RUnlock()
	if lengths == 1 || lengths == 2 || lengths == 0 {
		r.roomrwlock.Lock()
		r.roompls[rid].Players[lengths] = p
		r.plrooms[p.UserId] = rid
		r.roomrwlock.Unlock()
	}
	p.Room = m
	p.RoomId = m.Roomid
}

func (r *Rooms) OutRoom(p *Player, rid string) error {
	var err error
	ind := 10
	r.roomrwlock.RLock()
	m, ok := r.roompls[rid]
	if ok {
		if m.IsStart {
			err = errors.New("游戏已开始！")
		} else {
			for i, o := range m.Players {
				if o.UserId == p.UserId {
					ind = i
					break
				}
			}
		}
	}
	if r.plrooms[p.UserId] != rid {
		err = errors.New("用户不在指定房间！")
	}
	r.roomrwlock.RUnlock()
	if ind == 10 {
		err = errors.New("用户已经退出房间！")
	}
	if err != nil {
		return err
	}
	r.roomrwlock.Lock()
	r.roompls[rid].Players[ind] = nil
	delete(r.plrooms, p.UserId)
	r.roomrwlock.Unlock()
	p.Room = nil
	return nil
}

func (r *Rooms) GetRoom(rid string) *Room {
	r.roomrwlock.RLock()
	m := r.roompls[rid]
	r.roomrwlock.RUnlock()
	return m
}

func (r *Rooms) CheckPlayerExist(uid string) bool {
	r.roomrwlock.RLock()
	defer r.roomrwlock.RUnlock()
	_, ok := r.plrooms[uid]
	if ok {
		return true
	}
	return false
}
